package com.hp.utils.excel;

import cn.hutool.core.collection.CollUtil;
import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.hp.annotation.ExcelDataValidation;
import com.hp.enums.ExcelCellTypeEnum;
import com.hp.service.template_data.ExcelDynamicSelect;
import com.hp.utils.excel.handler.ConstraintSheetWriteHandler;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;


/**
 * @author hp
 */
@Slf4j
public class ExcelTemplateUtil {

    private ExcelTemplateUtil() {
    }


    /**
     * 导出模板
     *
     * @param filename 导出模板的文件名
     * @param clazz    实体类类型
     * @param response 响应对象
     */
    public static void exportTemplate(String filename, Class<?> clazz, HttpServletResponse response) {

        response.setContentType("application/vnd.ms-excel");
        response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(filename, StandardCharsets.UTF_8));
        response.setHeader("Cache-Control", "no-cache");
        response.setHeader("Pragma", "no-cache");
        response.setDateHeader("Expires", -1);
        response.setCharacterEncoding("UTF-8");

        try (ExcelWriter excelWriter = EasyExcelFactory.write(response.getOutputStream()).useDefaultStyle(false).build()) {
            WriteSheet writeSheet = writeSelectedSheet(clazz, 0, "sheet1");
            excelWriter.write(new ArrayList<>(), writeSheet);
            excelWriter.finish();
        } catch (IOException e) {
            log.error("导出模板异常：", e);
        }
    }


    /**
     * 创建即将导出的sheet页
     *
     * @param head      导出的表头信息和配置
     * @param sheetNo   sheet索引
     * @param sheetName sheet名称
     * @return sheet页
     */
    public static <T> WriteSheet writeSelectedSheet(Class<T> head, Integer sheetNo, String sheetName) {

        List<ExcelConstraintResolve> resolveList = resolveSelectedAnnotation(head);

        return EasyExcelFactory.writerSheet(sheetNo, sheetName)
                .head(head)
                .registerWriteHandler(new ConstraintSheetWriteHandler(resolveList))
                .build();
    }

    /**
     * 解析表头类中的下拉注解
     *
     * @param head 表头类
     * @param <T>  泛型
     * @return Map<下拉框列索引, 下拉框内容> map
     */
    private static <T> List<ExcelConstraintResolve> resolveSelectedAnnotation(Class<T> head) {

        List<ExcelConstraintResolve> result = new ArrayList<>();

        // getDeclaredFields(): 返回全部声明的属性；getFields(): 返回public类型的属性
        Field[] fields = head.getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            Field field = fields[i];
            // 解析注解信息
            ExcelDataValidation dataValidation = field.getAnnotation(ExcelDataValidation.class);
            ExcelProperty property = field.getAnnotation(ExcelProperty.class);
            if (Objects.isNull(dataValidation)) {
                continue;
            }
            // 获取列索引
            int index = (property != null && property.index() >= 0) ? property.index() : i;

            // 处理下拉类型
            if (StringUtils.equals(ExcelCellTypeEnum.SELECT.getType(), dataValidation.type())) {
                handleSelectType(dataValidation, result, index);
            }
            // 处理其他类型
            else {
                result.add(getResolve(dataValidation, dataValidation.type(), null, index));
            }


        }
        return result;
    }

    /**
     * 处理下拉类型
     */
    private static void handleSelectType(ExcelDataValidation dataValidation, List<ExcelConstraintResolve> result, int index) {
        // 自定义key
        String key = dataValidation.key();
        // 判断是否已经存在key，存在则直接将存在的复制一份
        Map<String, ExcelConstraintResolve> keyResolveMap = getSelectTemplateMap(result, key);
        if (CollUtil.isNotEmpty(keyResolveMap) && keyResolveMap.containsKey(key)) {
            ExcelConstraintResolve tempResolve = keyResolveMap.get(key);
            result.add(getResolve(dataValidation, ExcelCellTypeEnum.SELECT.getType(), tempResolve.getSource(), index));
            return;
        }
        // 获取下拉框数据
        String[] source = resolveSelectedSource(dataValidation);
        if (source.length > 0) {
            result.add(getResolve(dataValidation, ExcelCellTypeEnum.SELECT.getType(), source, index));
        }
    }


    /**
     * 拼装ExcelSelectedResolve对象
     */
    private static ExcelConstraintResolve getResolve(ExcelDataValidation dataValidation, String type, String[] source, int index) {
        ExcelConstraintResolve resolve = new ExcelConstraintResolve();
        resolve.setFirstRow(dataValidation.firstRow());
        resolve.setLastRow(dataValidation.lastRow());
        resolve.setSource(source);
        resolve.setType(type);
        resolve.setIndex(index);
        resolve.setKey(dataValidation.key());
        resolve.setTitle(dataValidation.title());
        resolve.setMinValue(dataValidation.minValue());
        resolve.setMaxValue(dataValidation.maxValue());
        return resolve;
    }


    /**
     * 将result转为map，重复的只保留第一个
     */
    private static Map<String, ExcelConstraintResolve> getSelectTemplateMap(List<ExcelConstraintResolve> result, String key) {
        return result.stream()
                .filter(bo -> ExcelCellTypeEnum.SELECT.getType().equals(bo.getType()))
                .filter(bo -> StringUtils.isNotBlank(bo.getKey()))
                .filter(bo -> key.equals(bo.getKey()))
                .collect(Collectors.toMap(
                        ExcelConstraintResolve::getKey,
                        Function.identity(),
                        (existing, replacement) -> existing
                ));
    }


    /**
     * 获取下拉框数据
     */
    private static String[] resolveSelectedSource(ExcelDataValidation excelDataValidation) {
        if (excelDataValidation == null) {
            return new String[0];
        }

        // 获取固定下拉框的内容
        String[] source = excelDataValidation.source();
        if (source.length > 0) {
            return source;
        }

        // 获取动态下拉框的内容
        Class<? extends ExcelDynamicSelect>[] classes = excelDataValidation.sourceClass();
        if (classes.length > 0) {
            try {
                ExcelDynamicSelect excelDynamicSelect = classes[0].getDeclaredConstructor().newInstance();
                String[] dynamicSelectSource = excelDynamicSelect.getSource();
                if (dynamicSelectSource != null && dynamicSelectSource.length > 0) {
                    return dynamicSelectSource;
                }
            } catch (InvocationTargetException | NoSuchMethodException | InstantiationException |
                     IllegalAccessException e) {
                log.error("解析动态下拉框数据异常", e);
            }
        }
        return new String[0];
    }
}